#include "Avalon/Serialization/JsonDeserializer.hpp"
#include <deque>
#include <boost/lexical_cast.hpp>
#pragma warning(disable : 4003)
#include <libjson.h>
#pragma warning(default : 4003)
#include "Avalon/IO/Buffer.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Serialization;
using namespace boost;
using namespace libjson;
using namespace std;

struct JsonDeserializer::Implementation {
  JSONNode m_root;
  const JSONNode* m_current;
  deque<const JSONNode*> m_nodes;
  int m_index;
  std::deque<int> m_sequences;

  Implementation()
    : m_current(NULL),
      m_index(0) {}

  const JSONNode* GetNode(const char* name) {
    if(m_current == NULL) {
      BOOST_THROW_EXCEPTION(SerializationException("Invalid JSON."));
    }
    if(name == NULL) {
      if(m_index >= static_cast<int>(m_current->size())) {
        BOOST_THROW_EXCEPTION(SerializationException(
          "Sequence index out of range: " + lexical_cast<string>(m_index)));
      }
      const JSONNode* valueNode = &(*m_current)[m_index];
      ++m_index;
      return valueNode;
    } else {
      JSONNode::const_iterator valueIterator = m_current->find(name);
      if(valueIterator == m_current->end()) {
        BOOST_THROW_EXCEPTION(SerializationException(
          "Value not found: " + string(name)));
      }
      return &(*valueIterator);
    }
  }
};

JsonDeserializer::JsonDeserializer()
    : m_imp(new Implementation()) {}

JsonDeserializer::~JsonDeserializer() {}

void JsonDeserializer::SetDataSource(const void* source, int size) {
  string data = string(static_cast<const char*>(source), size);
  m_imp->m_root = parse(data);
  m_imp->m_current = &m_imp->m_root;
  m_imp->m_nodes.push_front(m_imp->m_current);
}

void JsonDeserializer::Shuttle(const char* name, bool& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_BOOL) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = node->as_bool();
}

void JsonDeserializer::Shuttle(const char* name, char& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = static_cast<char>(node->as_int());
}

void JsonDeserializer::Shuttle(const char* name, unsigned char& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = static_cast<unsigned char>(node->as_int());
}

void JsonDeserializer::Shuttle(const char* name, short& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = static_cast<short>(node->as_int());
}

void JsonDeserializer::Shuttle(const char* name, unsigned short& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = static_cast<unsigned short>(node->as_int());
}

void JsonDeserializer::Shuttle(const char* name, int& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = node->as_int();
}

void JsonDeserializer::Shuttle(const char* name, unsigned int& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = lexical_cast<unsigned int>(node->as_string());
}

void JsonDeserializer::Shuttle(const char* name, long& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = node->as_int();
}

void JsonDeserializer::Shuttle(const char* name, unsigned long& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = lexical_cast<unsigned long>(node->as_string());
}

void JsonDeserializer::Shuttle(const char* name, long long& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = lexical_cast<long long>(node->as_string());
}

void JsonDeserializer::Shuttle(const char* name, unsigned long long& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = lexical_cast<unsigned long long>(node->as_string());
}

void JsonDeserializer::Shuttle(const char* name, float& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = static_cast<float>(node->as_float());
}

void JsonDeserializer::Shuttle(const char* name, double& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_NUMBER) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = node->as_float();
}

void JsonDeserializer::Shuttle(const char* name, std::string& value) {
  const JSONNode* node = m_imp->GetNode(name);
  if(node->type() != JSON_STRING) {
    BOOST_THROW_EXCEPTION(SerializationException("Type mismatch."));
  }
  value = node->as_string();
}

void JsonDeserializer::Shuttle(const char* name, Buffer& data) {
  if(m_imp->m_current == NULL) {
    BOOST_THROW_EXCEPTION(SerializationException("Invalid JSON."));
  }
  const JSONNode* node;
  if(name == NULL) {
    if(m_imp->m_index >= static_cast<int>(m_imp->m_current->size())) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Sequence index out of range: " +
        lexical_cast<string>(m_imp->m_index)));
    }
    node = &(*m_imp->m_current)[m_imp->m_index];
    ++m_imp->m_index;
  } else {
    JSONNode::const_iterator nodeIterator = m_imp->m_current->find(name);
    if(nodeIterator == m_imp->m_current->end()) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Value not found: " + string(name)));
    }
    node = &(*nodeIterator);
  }
  data.Reset();
  string decodedData = node->as_binary();
  data.Write(0, decodedData.c_str(), decodedData.size());
}

void JsonDeserializer::StartStructure(const char* name) {
  if(m_imp->m_current == NULL) {
    BOOST_THROW_EXCEPTION(SerializationException("Invalid JSON."));
  }
  if(m_imp->m_current->type() == JSON_ARRAY) {
    if(m_imp->m_index >= static_cast<int>(m_imp->m_current->size())) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Array index out of range: " +
        lexical_cast<string>(m_imp->m_index)));
    }
    const JSONNode* structure = &(*m_imp->m_current)[m_imp->m_index];
    m_imp->m_current = structure;
    ++m_imp->m_index;
    m_imp->m_nodes.push_front(m_imp->m_current);
  } else {
    assert(name != NULL);
    JSONNode::const_iterator nodeIterator = m_imp->m_current->find(name);
    if(nodeIterator == m_imp->m_current->end()) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Value not found: " + string(name)));
    }
    m_imp->m_current = &(*nodeIterator);
    m_imp->m_nodes.push_front(m_imp->m_current);
  }
}

void JsonDeserializer::EndStructure() {
  m_imp->m_nodes.pop_front();
  m_imp->m_current = m_imp->m_nodes.front();
}

void JsonDeserializer::StartSequence(const char* name) {
  if(m_imp->m_current == NULL) {
    BOOST_THROW_EXCEPTION(SerializationException("Invalid JSON."));
  }
  if(m_imp->m_current->type() == JSON_ARRAY) {
    if(m_imp->m_index >= static_cast<int>(m_imp->m_current->size())) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Array index out of range: " +
        lexical_cast<string>(m_imp->m_index)));
    }
    const JSONNode* sequence = &(*m_imp->m_current)[m_imp->m_index];
    m_imp->m_current = sequence;
    ++m_imp->m_index;
    m_imp->m_nodes.push_front(m_imp->m_current);
    m_imp->m_sequences.push_front(m_imp->m_index);
    m_imp->m_index = 0;
  } else {
    JSONNode::const_iterator nodeIterator = m_imp->m_current->find(name);
    if(nodeIterator == m_imp->m_current->end()) {
      BOOST_THROW_EXCEPTION(SerializationException(
        "Value not found: " + string(name)));
    }
    m_imp->m_current = &(*nodeIterator);
    m_imp->m_nodes.push_front(m_imp->m_current);
    m_imp->m_sequences.push_front(m_imp->m_index);
    m_imp->m_index = 0;
  }
}

void JsonDeserializer::EndSequence() {
  m_imp->m_nodes.pop_front();
  m_imp->m_current = m_imp->m_nodes.front();
  m_imp->m_index = m_imp->m_sequences.front();
  m_imp->m_sequences.pop_front();
}
